
package nsga;

/**
 * Problema Sistema de Caracterización Inteligende de Carga de Transformadores
 * de Distribución
 *
 * @author Gustavo Planás
 */

import java.io.BufferedReader;
import java.io.FileReader;

public class SCICTD implements Problema 
{

    private int n;  // Cantidad de transformadores.
    private int T;  // Cantidad de muestras.
    private double[][] mediciones; // Matriz de mediciones reales realizadas.
    private int TIPO = 3;  // Es el Tipo correspondiente al problema SCICTD.

    //
    // Método Contructor.
    //
    public SCICTD(double[][] mediciones) 
    {
        this.mediciones = mediciones;
        n = mediciones[0].length;
        T = mediciones.length;
    }

    //
    // Asigna el Fitness a un Individuo de la población.
    //
    @Override
    public void setFitness(Individuo individuo) 
    {
        // Se crea el vector fitness de dos elementos: uno para cada objetivo.
        double[] fitness = new double[6];
        
        // CALCULO DEL FITNESS (Error promedio)
        
        // PASO 0: Obtener datos del individuo.
        int[] nodos = ((IndividuoSCICTD) individuo).getNodos();
        int k = ((IndividuoSCICTD) individuo).getK(); // Cantidad de medidores.
        
        // PASO 1: Generar derivadas de errores (Sistemas de ecuaciones).
        // Se guardan las posiciones de los medidores en el vector posiciones.
        int[] posiciones = new int[k];
        int pos = 0;
        for (int i = 0; i < n; i++) 
        {
            if (nodos[i] == 1) 
            {
                posiciones[pos] = i;
                pos++;
            }
        }
        // Se busca el valor desconocido, es decir, el Transformador que no tiene asignado medidor.
        fitness[0] = 0;
        int desconocido = 0;
        for (int d = 0; d < n; d++) 
        {
            // Se calculan los errores para cada transformador que no tiene medidor.
            double errorCuad = 0;
            double errorAbs  = 0;
            double errorRel  = 0;
            double errorMax  = 0;
            double errRelMax = 0;
            if (nodos[d] == 0)
            {
                // Matriz de ecuaciones inicializada a ceros.
                double[][] ecuaciones = new double[k][k + 1];
                for (int i = 0; i < k; i++) 
                    for (int j = 0; j < k + 1; j++) 
                        ecuaciones[i][j] = 0;
                // Vector de factores aij inicializada a ceros.
                double[] factores = new double[k];
                for (int i = 0; i < k; i++) 
                    factores[i] = 0;
                desconocido = d;
                // Se arman las ecuaciones.
                int pivot;
                for (int i = 0; i < k; i++) 
                {
                    pivot = posiciones[i];
                    for (int j = 0; j < k + 1; j++) 
                    {
                        for (int l = 0; l < T; l++) 
                        {
                            if (j == k) 
                                ecuaciones[i][j] += mediciones[l][pivot] * mediciones[l][desconocido];
                            else 
                                ecuaciones[i][j] += mediciones[l][pivot] * mediciones[l][posiciones[j]];
                        }
                    }
                }

                // PASO 2: Resolver el Sistema de ecuaciones por el método de Gauss.
                // Escalonar la matriz de ecuaciones.
                int col = k + 1;
                double factor;
                for(int f = 0; f < k - 1; f++)
                {
                    factor = ecuaciones[f][f];
                    for(int j = 0; j < col; j++)
                        ecuaciones[f][j] = ecuaciones[f][j] / factor;
                    for(int i = f + 1; i < k; i++)
                    {
                        factor = ecuaciones[i][f] / ecuaciones[f][f];
                        for(int j = f; j < col; j++)
                            ecuaciones[i][j] = ecuaciones[i][j] - (factor * ecuaciones[f][j]);
                    }
                }
                int f = k - 1;
                factor = ecuaciones[f][f];
                for(int j = 0; j < col; j++)
                    ecuaciones[f][j] = ecuaciones[f][j] / factor;
                // Reemplazar los valores hallados en la matriz para encontrar los factores.
                int fac = f;
                for(int i = f; i >= 0; i--)
                {
                    factores[i] = ecuaciones[i][k];
                    for(int c = f; c > fac; c--)
                        factores[i] -= ecuaciones[i][c] * factores[c];
                    fac--;
                }

                // PASO 3: Hallar el error promedio elevado al Cuadrado.
                double real;
                double error;
                double abs;
                double relativo;
                for (int l = 0; l < T; l++) 
                {
                    real  = mediciones[l][desconocido];
                    error = real;
                    for(int i = 0; i < k; i++)
                        error -= factores[i] * mediciones[l][posiciones[i]];
                    // Se calcula la sumatoria de los errores de cada medición.
                    errorCuad += Math.pow(error, 2.0);
                    abs = Math.abs(error);
                    errorAbs += abs;
                    relativo = 100 * abs / real;
                    errorRel += relativo;
                    if(abs > errorMax)
                        errorMax = abs;
                    if(relativo > errRelMax)
                        errRelMax = relativo;
                }
                // Se suman los errores medios del conjunto de muestras de cada transformador desconocido.
                fitness[0] += errorCuad / T;
                fitness[1] += errorAbs / T;
                fitness[2] += errorRel / T;
                if(errorMax > fitness[3])
                    fitness[3] = errorMax;
                if(errRelMax > fitness[4])
                    fitness[4] = errRelMax;
                // FIN DEL CALCULO de Error promedio.
            }
        }
        // Se promedia el error de todos los transformadores desconocidos.
        fitness[0] = fitness[0] / (n - k);
        fitness[1] = fitness[1] / (n - k);
        fitness[2] = fitness[2] / (n - k);
        
        // El último objetivo es igual a k / n.
        fitness[5] = (double) k / n;
        // Almacena el Fitness del Individuo.
        individuo.setFitness(fitness);
        
        // 0 => Error Cuadrático Promedio
        // 1 => Error Absoluto Promedio
        // 2 => Error Relativo Promedio
        // 3 => Error Absoluto Máximo
        // 4 => Error Relativo Máximo
        // 5 => n/k
    }

    //
    // Cruza dos individuos con un Punto de Corte.
    //
    @Override
    public Individuo[] cruzar(Individuo individuo1, Individuo individuo2) 
    {
        // Crear 2 individuos Hijos con sus respectivos nodos.
        IndividuoSCICTD[] hijos = new IndividuoSCICTD[2];
        hijos[0] = new IndividuoSCICTD(n);
        hijos[1] = new IndividuoSCICTD(n);
        int[] nodosH1 = new int[n];
        int[] nodosH2 = new int[n];
        // Seleccionar puntos de Corte.
        double puntoCorte1 = Math.random();
        double nodo = 1.0 / n;
        double d = 0;
        int i = -1;
        while (d <= puntoCorte1) 
        {
            d += nodo;
            i++;
        }
        double puntoCorte2 = Math.random();
        d = 0;
        int j = -1;
        while (d <= puntoCorte2) 
        {
            d += nodo;
            j++;
        }
        // Se hace un intercambio de manera a que el punto de corte 1 siempre sea menor.
        if (i > j)
        {
            int aux = i;
            i = j;
            j = aux;
        }
            
        // Obtener nodos de los individuos Padres.
        int[] nodosP1 = ((IndividuoSCICTD) individuo1).getNodos();
        int[] nodosP2 = ((IndividuoSCICTD) individuo2).getNodos();
        // Los nodos desde 1 hasta i y j + 1 del Padre1 van al Individuo1.
        // Los nodos entre i y j del Padre2 van al Individuo1.
        // Los nodos desde 1 hasta i y j + 1 del Padre2 van al Individuo2.
        // Los nodos entre i y j del Padre1 van al Individuo2.
        for (int k = 0; k < n; k++) 
        {
            if (k <= i || k > j) 
            {
                nodosH1[k] = nodosP1[k];
                nodosH2[k] = nodosP2[k];
            } 
            else 
            {
                nodosH1[k] = nodosP2[k];
                nodosH2[k] = nodosP1[k];
            }
        }
        hijos[0].setNodos(nodosH1);
        hijos[1].setNodos(nodosH2);
        return hijos;
    }

    //
    // Devuelve cantidad de transformadores.
    //
    @Override
    public int getLength() 
    {
        return n;
    }

    //
    // Devuelve tipo de problema.
    //
    @Override
    public int getTipo() 
    {
        return TIPO;
    }

    //
    // Lee el archivo txt con los datos de los transformadores y las mediciones.
    //
    public static SCICTD main(String args) throws Exception 
    {
        // Se pasa por parámetro el archivo a ser leído.
        BufferedReader bufferedReader = new BufferedReader(new FileReader(args));
        // En la primera línea se obtiene la cantidad de transformadores.
        int n = Integer.parseInt(bufferedReader.readLine());
        // En la segunda línea se obtiene la cantidad de muestras.
        int T = Integer.parseInt(bufferedReader.readLine());
        // Se crea la matriz de medicions que mide n x T.
        double[][] mediciones = new double[T][n];
        // Se leen las mediciones de cada transformador en el archivo.
        for (int i = 0; i < T; i++) 
        {
            String[] fila = bufferedReader.readLine().split(";");
            for (int j = 0; j < n; j++)
                mediciones[i][j] = Double.parseDouble(fila[j]);
        }
        
        return new SCICTD(mediciones);
    }
}
